//////////////////////////////////////////////////////////////////
// (c) Copyright 2010-  by Jeongnim Kim
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//   National Center for Supercomputing Applications &
//   Materials Computation Center
//   University of Illinois, Urbana-Champaign
//   Urbana, IL 61801
//   e-mail: jnkim@ncsa.uiuc.edu
//
// Supported by 
//   National Center for Supercomputing Applications, UIUC
//   Materials Computation Center, UIUC
//////////////////////////////////////////////////////////////////
// -*- C++ -*-
/**@file a2a_bench.cpp
 */
#include <fft3d/fft3d_engine_a2a.hpp>
#include <fft3d/fft3d_engine_a2av.hpp>
//#define DOSTUFF

inline void print_help(const std::string& msg)
{
  printf("%s -s fft_dim -y mpigridY -i iterations \n",msg.c_str());
}

namespace mpi
{
  template<typename ENG>
    struct a2atest
    {
      static void doit(communicator& mycomm, int fftsize, int npy, int niters, const char* tag)
      {
        mpi::FFT3DEngineIA2A<double,ENG> decomp(fftsize,npy,mycomm);

        decomp.fft_forward(1);
        decomp.fft_backward(1);

        decomp.reset();

        qmcplusplus::Timer c;
        for(int iter=0; iter<niters; ++iter)
        {
          decomp.fft_backward(6);
          decomp.spectrum();
          decomp.fft_forward(3);
          decomp.rk4();
        }
        double dt=c.elapsed();
        mpi::reduce(mycomm,dt);
        mpi::reduce(mycomm,decomp.wtime[0]);
        mpi::reduce(mycomm,decomp.wtime[1]);

        if(mycomm.rank()==0) 
        {
          double factor=1.0/static_cast<double>(mycomm.size()*niters);
          printf("%s %4d %4d %4d %4d %10d %10d %12.4e %12.4e %12.4e\n"
              ,tag,fftsize,mycomm.size(),npy,mycomm.size()/npy
              ,static_cast<int>(decomp.msgSize[0]*sizeof(double)), static_cast<int>(decomp.msgSize[1]*sizeof(double))
              ,dt*factor,decomp.wtime[0]*factor,decomp.wtime[1]*factor);
        }
      }
    };
}

int main(int argc, char** argv)
{

  mpi::environment env(argc,argv);
  mpi::communicator mycomm;

  int fftsize=786;
  int npy=16;
  int niters=5;

  bool debug=false;
  int opt;
  while((opt = getopt(argc, argv, "hds:y:i:")) != -1) {
    switch(opt) {
      case 'h':
        print_help("Help Message");
        return 1;
      case 'd':
        debug=true;
        break;
      case 's'://problem size
        fftsize=atoi(optarg);
        break;
      case 'y': //processor grid
        npy=atoi(optarg);
        break;
      case 'i': //processor grid
        niters=atoi(optarg);
        break;
      default:
        print_help("Unknown options");
        return 1;
    }
  }

  if(mycomm.rank()==0) 
    printf("#fftsize tasks npy npy msgYZ msgXY elapsed rowTrans colTrans\n");

  mpi::a2atest<mpi::ia2a_a2a>::doit(mycomm,fftsize,npy,niters,         "A2A    ");
  mpi::a2atest<mpi::ia2a_dummy>::doit(mycomm,fftsize,npy,niters,       "asyncS ");
  mpi::a2atest<mpi::ia2a_pairexchange>::doit(mycomm,fftsize,npy,niters,"asyncPE");

  {
    mpi::FFT3DEngineA2AV<double> decomp(fftsize,npy,mycomm);

    decomp.fft_forward(1);
    decomp.fft_backward(1);

    decomp.reset();

    qmcplusplus::Timer c;
    for(int iter=0; iter<niters; ++iter)
    {
      decomp.fft_backward(6);
      decomp.spectrum();
      decomp.fft_forward(3);
      decomp.rk4();
    }
    double dt=c.elapsed();
    mpi::reduce(mycomm,dt);
    mpi::reduce(mycomm,decomp.wtime[0]);
    mpi::reduce(mycomm,decomp.wtime[1]);

    if(mycomm.rank()==0) 
    {
      double factor=1.0/static_cast<double>(mycomm.size()*niters);
      printf("%s %4d %4d %4d %4d %10d %10d %12.4e %12.4e %12.4e\n"
          ,"A2AV   ", fftsize,mycomm.size(),npy,mycomm.size()/npy
          ,static_cast<int>(decomp.msgSize[0]*sizeof(double)), static_cast<int>(decomp.msgSize[1]*sizeof(double))
          ,dt*factor,decomp.wtime[0]*factor,decomp.wtime[1]*factor);
    }
  }
  //int npx=mycomm.size()/npy;
  //int i=mycomm.rank()/npy;
  //int j=mycomm.rank()%npy;
  //PencilDecomp decomp(fftsize,npx,npy,i,j);
  //decomp.setCommunicator(mycomm);

  return 0;
}

/***************************************************************************
 * $RCSfile$   $Author: jnkim $
 * $Revision: 1770 $   $Date: 2007-02-17 17:45:38 -0600 (Sat, 17 Feb 2007) $
 * $Id: fft2d.cpp 1770 2007-02-17 23:45:38Z jnkim $ 
 ***************************************************************************/
